package com.handle.common.redis.aspect;

import com.handle.common.redis.annotation.Idempotent;
import com.handle.common.redis.util.RedissonUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import javax.xml.ws.Response;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
@AllArgsConstructor
public class IdempotentAspect {

    private RedissonUtil redissonUtil;

    /**
     * SPEL表达式解析器
     */
    private final SpelExpressionParser parser = new SpelExpressionParser();

    private LocalVariableTableParameterNameDiscoverer discoverer;

    @Around(value = "@annotation(com.handle.common.redis.annotation.Idempotent)")
    public Object doNoRepeatAround(ProceedingJoinPoint point) throws Throwable {
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        Idempotent annotation = method.getAnnotation(Idempotent.class);
        String key = parseKey(point, annotation);
        RLock lock = redissonUtil.getLock(annotation.lockName() + key);
        boolean isLocked = false;
        try {
            isLocked = redissonUtil.tryLock(key, TimeUnit.SECONDS, annotation.waitTime());
            // 如果成功获取到锁就继续执行
            if (isLocked) {
                // 执行进程
                return point.proceed();
            } else {
                // 未获取到锁
                throw new RuntimeException("请勿重复提交");
            }
        } finally {
            // 如果锁还存在，在方法执行完成后，释放锁
            if (isLocked) {
                redissonUtil.unlock(lock);
            }
        }
        return point.proceed();
    }


    private String parseKey(ProceedingJoinPoint pjp, Idempotent noRepeat) {
        Object[] args = pjp.getArgs();
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        String[] params = discoverer.getParameterNames(method);
        EvaluationContext context = new StandardEvaluationContext();
        for (int len = 0; len < Objects.requireNonNull(params).length; len++) {
            context.setVariable(params[len], args[len]);
        }
        String keySpel = noRepeat.key();
        Expression keyExpression = parser.parseExpression(keySpel);
        return keyExpression.getValue(context, String.class);

    }

}
